package com.huan.fastdfs.controller;

import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * fastdfs 文件上传和下载控制器
 *
 * @author huan.fu
 * @date 2022/10/8 - 20:24
 */
@RestController
@AllArgsConstructor
@RequestMapping("fdfs")
@Slf4j
public class FastdfsController {

    private final FastFileStorageClient fastFileStorageClient;
    private final FdfsWebServer fdfsWebServer;

    /**
     * 上传文件
     */
    @PostMapping("uploadFile")
    public List<String> uploadFile(MultipartFile file) throws IOException, NoSuchAlgorithmException {
        String fileName = file.getOriginalFilename();
        // 获取文件扩展名
        String extension = FilenameUtils.getExtension(fileName);
        // 文件元数据信息
        Set<MetaData> metaData = new HashSet<>(4);
        metaData.add(new MetaData("fileName", fileName));
        // 上传文件
        StorePath storePath = fastFileStorageClient.uploadImageAndCrtThumbImage(file.getInputStream(), file.getSize(), extension, metaData);
        log.info("文件上传路径:[{}]", storePath.getFullPath());
        String viewPath = fdfsWebServer.getWebServerUrl() + storePath.getFullPath();
        log.info("可访问路径:[{}]", viewPath);

        extension = FilenameUtils.getExtension(viewPath);
        String xthumbPath = viewPath.replace("." + extension, "") + "_150x150." + extension;
        log.info("缩略图路径:[{}]", xthumbPath);

        long ts = System.currentTimeMillis() / 1000;

        List<String> result = new ArrayList<>(3);
        result.add(viewPath + "?token=" + generatorToken(storePath.getFullPath(), ts) + "&ts=" + ts);
        result.add(xthumbPath + "?token=" + generatorToken(storePath.getFullPath(), ts) + "&ts=" + ts);
        result.add(storePath.getFullPath());

        return result;
    }

    /**
     * 生成token
     *
     * @param fileId          the filename return by FastDFS server,不能含有组
     * @param timestampSecond 时间戳 单位秒
     * @return token
     * @throws NoSuchAlgorithmException
     */
    private String generatorToken(String fileId, Long timestampSecond) throws NoSuchAlgorithmException {
        // 需要去掉 group
        fileId = fileId.substring(fileId.indexOf("/") + 1);
        byte[] bsFilename = fileId.getBytes(StandardCharsets.UTF_8);
        byte[] bsTimestamp = timestampSecond.toString().getBytes(StandardCharsets.UTF_8);
        // thisisasecuritykey 需要和 /etc/fdfs/http.conf 中的 http.anti_steal.secret_key 值一致
        byte[] bsKey = "thisisasecuritykey".getBytes(StandardCharsets.UTF_8);

        byte[] buff = new byte[bsFilename.length + bsKey.length + bsTimestamp.length];
        System.arraycopy(bsFilename, 0, buff, 0, bsFilename.length);
        System.arraycopy(bsKey, 0, buff, bsFilename.length, bsKey.length);
        System.arraycopy(bsTimestamp, 0, buff, bsFilename.length + bsKey.length, bsTimestamp.length);

        return md5(buff);
    }

    public static String md5(byte[] source) throws NoSuchAlgorithmException {
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        md.update(source);
        byte tmp[] = md.digest();
        char str[] = new char[32];
        int k = 0;
        for (int i = 0; i < 16; i++) {
            str[k++] = hexDigits[tmp[i] >>> 4 & 0xf];
            str[k++] = hexDigits[tmp[i] & 0xf];
        }

        return new String(str);
    }

    /**
     * 下载文件
     */
    @GetMapping("download")
    public void downloadFile(String filePath, HttpServletResponse response) throws IOException, NoSuchAlgorithmException {
        log.info("需要下载的文件:[{}]", filePath);
        String group = filePath.substring(0, filePath.indexOf("/"));
        String path = filePath.substring(filePath.indexOf("/") + 1);
        Set<MetaData> metadata = fastFileStorageClient.getMetadata(group, path);
        String fileName = metadata.iterator().next().getValue();
        byte[] bytes = fastFileStorageClient.downloadFile(group, path, new DownloadByteArray());
        response.setContentType("application/octet-stream");
        response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, Charsets.UTF_8.displayName()));
        IOUtils.write(bytes, response.getOutputStream());
    }
}
